package com.ms.redis.autoconfig;

import com.google.common.collect.Lists;
import com.ms.common.utils.StringUtils;
import com.ms.redis.service.RedisService;
import com.ms.redis.service.impl.RedisServiceImpl;
import com.ms.redis.service.impl.RedisWatcherLockService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class RedisAutoConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(RedisAutoConfiguration.class);

    private static final String REDIS_MODE_SENTINEL = "sentinel";

    private static final String REDIS_MODE_CLUSTER = "cluster";

    @Value("${mdb.redis.host:127.0.0.1}")
    private String redisHost;

    @Value("${mdb.redis.port:6379}")
    private String redisPort;

    @Value("${mdb.redis.password:}")
    private String redisPassword;

    @Value("${mdb.redis.database: 0}")
    private String redisDatabase;

    @Value("${mdb.redis.maxTotal:500}")
    private String redisMaxTotal;

    @Value("${mdb.redis.maxIdle:500}")
    private String redisMaxIdle;

    @Value("${mdb.redis.minIdle:10}")
    private String redisMinIdle;

    @Value("${mdb.redis.maxWaitMillis:}")
    private String redisMaxWaitMillis;

    @Value("${mdb.redis.timeout:3000}")
    private String redisTimeout;

    @Value("${mdb.redis.sentinelMaster:}")
    private String sentinelMaster;

    @Value("${mdb.redis.sentinelNodes:}")
    private String sentinelNodes;

    @Value("${mdb.redis.cluster.nodes:}")
    private String clusterNodes;

    @Value("${mdb.redis.mode: standalone}")
    private String redisMode;

    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Integer.parseInt(redisMaxTotal));
        config.setMaxIdle(Integer.parseInt(redisMaxIdle));
        config.setMinIdle(Integer.parseInt(redisMinIdle));
        config.setMaxWaitMillis(Integer.parseInt(redisMaxWaitMillis));
        config.setTestOnBorrow(false);
        config.setTestWhileIdle(false);
        config.setTestOnReturn(false);
        return config;
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        logger.info("=============redis mode ============ {}", redisMode);
        JedisConnectionFactory jedisconnectionFactory;
        switch (redisMode) {
            case REDIS_MODE_SENTINEL:
                jedisconnectionFactory = getSentinelFactory();
                break;
            case REDIS_MODE_CLUSTER:
                jedisconnectionFactory = getClusterFactory();
                break;
            default:
                jedisconnectionFactory = getStandaloneFactory();
                break;
        }
        jedisconnectionFactory.setTimeout(Integer.parseInt(redisTimeout));
        jedisconnectionFactory.setUsePool(true);
        jedisconnectionFactory.setPoolConfig(jedisPoolConfig());
        logger.info("连接对象 {}", jedisconnectionFactory.getConnection().getNativeConnection());
        return jedisconnectionFactory;
    }

    /**
     * 获取哨兵模式下配置连接工厂
     *
     * @return redis 连接工厂
     */
    private JedisConnectionFactory getSentinelFactory() {
        logger.info("============= getSentinelFactory start ============");
        JedisConnectionFactory factory = new JedisConnectionFactory(redisSentinelConfig(), jedisPoolConfig());
        factory.afterPropertiesSet();
        logger.info("============= getSentinelFactory end ==============");
        return factory;
    }

    /**
     * 获取哨兵模式下配置连接工厂
     *
     * @return redis 配置
     */
    @Bean
    public RedisSentinelConfiguration redisSentinelConfig() {
        logger.info("spring.redis.sentinel.master========" + sentinelMaster);
        logger.info("spring.redis.sentinel.nodes========" + sentinelNodes);
        if (!StringUtils.isNotEmpty(sentinelMaster) && !StringUtils.isNotEmpty(sentinelNodes)) {
            throw new RuntimeException("Redis哨兵模式下节点不能为空");
        }
        RedisSentinelConfiguration sentinelConfig = getRedisSentinelConfiguration();
        logger.info("sentinelConfig========" + sentinelConfig.getSentinels());
        return sentinelConfig;
    }

    /**
     * 获取Sentinel模式配置
     *
     * @return redis 配置
     */
    private RedisSentinelConfiguration getRedisSentinelConfiguration() {
        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
        sentinelConfig.setMaster(sentinelMaster);
        if (StringUtils.isNotEmpty(redisDatabase)) {
            sentinelConfig.setDatabase(Integer.parseInt(redisDatabase));
        }
        String[] node = sentinelNodes.split(",");
        for (String s : node) {
            String[] parts = s.split(":", 2);
            if (parts.length == 2) {
                sentinelConfig.sentinel(parts[0], Integer.parseInt(parts[1]));
            }
        }
        return sentinelConfig;
    }

    /**
     * 获取集群模式下配置连接工厂
     *
     * @return redis 连接工厂
     */
    private JedisConnectionFactory getClusterFactory() {
        logger.info("==========getClusterFactory init start============");
        if (StringUtils.isEmpty(clusterNodes)) {
            throw new RuntimeException("Redis集群模式下节点不能为空");
        }
        RedisClusterConfiguration configuration = new RedisClusterConfiguration(Lists.newArrayList(clusterNodes));
        if (StringUtils.isNotEmpty(redisPassword)) {
            configuration.setPassword(redisPassword);
        }
        JedisConnectionFactory factory = new JedisConnectionFactory(configuration, jedisPoolConfig());
        factory.afterPropertiesSet();
        logger.info("===========getClusterFactory init end============");
        return factory;
    }

    /**
     * 获取单机模式下配置连接工厂
     *
     * @return redis 连接工厂
     */
    private JedisConnectionFactory getStandaloneFactory() {
        logger.info("=============getStandaloneFactory init start==========");
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(redisHost);
        redisStandaloneConfiguration.setPort(Integer.parseInt(redisPort));
        if (StringUtils.isNotEmpty(redisPassword)) {
            redisStandaloneConfiguration.setPassword(redisPassword);
        }
        if (StringUtils.isNotEmpty(redisDatabase)) {
            redisStandaloneConfiguration.setDatabase(Integer.parseInt(redisDatabase));
        }
        JedisConnectionFactory factory = new JedisConnectionFactory(redisStandaloneConfiguration);
        factory.afterPropertiesSet();
        logger.info("=============getStandaloneFactory init end============");
        return factory;
    }

    /**
     * redisTemplate 操作模板
     *
     * @return reds操作模板
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 分布式锁公共注入
     *
     * @return redis分布式锁操作工具类
     */
    @Bean
    public RedisWatcherLockService redisWatcherLockService() {
        return new RedisWatcherLockService();
    }

    /**
     * redis
     *
     * @return redis分布式锁操作工具类
     */
    @Bean
    public RedisService redisService() {
        return new RedisServiceImpl();
    }

}
